﻿using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using MyTRCP.Common.MyWave;
using NAudio.Wave;
using System.Collections.Concurrent;

namespace MyTRCP.Common.Services
{
    public class AudioPlayService : BackgroundService
    {
        private readonly ILogger<AudioPlayService> _logger;
        private const int PER_SECTION_DURATION_MS = 28;
        private const int CACHE_SECTION_NUMBER = 1;
        private readonly BlockingQueue<byte[]> _asQueue;
        private readonly AutoResetEvent _playDoneEvent;
        private volatile MyWaveOutEvent? mwoe;
        private readonly ConcurrentQueue<byte[]> _waveOutQueue;
        private volatile int _devId;
        public AudioPlayService(ILogger<AudioPlayService> logger)
        {
            logger.LogDebug($"AudioPlayService creating...");
            _logger = logger;
            _asQueue = new BlockingQueue<byte[]>();
            _playDoneEvent = new AutoResetEvent(false);
            _waveOutQueue = new ConcurrentQueue<byte[]>();
            _devId = -1;
        }
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            await Task.Run(() => AudioPlayThreadMethod(stoppingToken), stoppingToken);
        }
        public BlockingQueue<byte[]> PcmQueue
        {
            get
            {
                return _asQueue;
            }
        }
        public Dictionary<int, WaveOutCapabilities> GetWaveOutDevCapDic()
        {
            int count = MyWaveOutEvent.DeviceCount;
            Dictionary<int, WaveOutCapabilities> dic = new Dictionary<int, WaveOutCapabilities>();
            for (int i = 0; i < count; i++)
            {
                WaveOutCapabilities woc = MyWaveOutEvent.GetCapabilities(i);
                dic.Add(i, woc);
            }
            return dic;
        }
        public bool ChangePlayDevice(int devId)
        {
            _devId = devId;
            if (mwoe != null)
            {
                try
                {
                    lock (mwoe)
                    {
                        mwoe.DeviceNumber = _devId;
                        mwoe.Init(_waveOutQueue, new WaveFormat(32000, 16, 1));
                    }
                    return true;
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, $"ChangePlayDevice Exception:{ex.Message}");
                }
            }
            else
            {
                return true;
            }
            return false;
        }
        private void AudioPlayThreadMethod(CancellationToken stoppingToken)
        {
            _logger.LogInformation($"音频播放线程开始");
            try
            {
                mwoe = new MyWaveOutEvent();
                mwoe.PlaybackStopped += Mwoe_PlaybackStopped;
                mwoe.Init(_waveOutQueue, new WaveFormat(32000, 16, 1));
                while (!stoppingToken.IsCancellationRequested)
                {
                    try
                    {
                        byte[] section = _asQueue.Take();
                        if (section.Length == 1)
                            continue;
                        _waveOutQueue.Enqueue(section);
                        if (section.Length > 0)
                        {
                            if (_waveOutQueue.Count > CACHE_SECTION_NUMBER)
                            {
                                mwoe.Play();
                            }
                        }
                        else
                        {
                            if (_waveOutQueue.Count <= CACHE_SECTION_NUMBER)
                            {
                                if (mwoe.PlaybackState != PlaybackState.Playing)
                                    _waveOutQueue.Clear();
                            }
                        }
                    }
                    catch (OperationCanceledException)
                    {
                        _logger.LogInformation("AudioPlayThreadMethod in-loop Received OperationCanceledException,break.");
                        break;
                    }
                    catch (Exception e)
                    {
                        _logger.LogError(e, $"AudioPlayThreadMethod in-loop Exception:{e.Message}");
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"AudioPlayThreadMethod Exception:{ex.Message}");
            }
            mwoe?.Dispose();
            mwoe = null;
            _logger.LogInformation($"音频播放线程结束");
        }

        private void Mwoe_PlaybackStopped(object? sender, StoppedEventArgs e)
        {
            if (e.Exception != null)
            {
                _logger.LogError(e.Exception, $"play stopped during excetpion:{e.Exception.Message}");
            }
        }
        public override Task StartAsync(CancellationToken cancellationToken)
        {
            _logger.LogDebug("startasync called");
            return base.StartAsync(cancellationToken);
        }
        public override Task StopAsync(CancellationToken cancellationToken)
        {
            _asQueue.CancelTake();
            return base.StopAsync(cancellationToken);
        }
    }
}
